﻿using System;
using System.Collections.Generic;
using System.Reflection;
using CSW.Framework.Common;
using CSW.Framework.Linq.SchemaModel;
using CSW.Framework.Linq.QueryModel;
using Query = CSW.Framework.Linq.QueryModel.Query;

namespace CSW.Framework.Linq
{
    /// <summary>
    /// LoadFromDataRow LoadVariables
    /// </summary>
    public class LoadVariables<T> : ILoadVariables
        where T : IDatabase, new()
    {
        private static readonly Dictionary<Query, Dictionary<Type, ILoadVariables>> m_LoadVariablesDictionary = new Dictionary<Query, Dictionary<Type, ILoadVariables>>();
        private static readonly object m_LoadVariablesDictionaryLocker = new object();

        /// <summary>
        /// Gets the load variables dictionary.
        /// </summary>
        /// <value>The load variables dictionary.</value>
        public static Dictionary<Query, Dictionary<Type, ILoadVariables>> LoadVariablesDictionary
        {
            get { return m_LoadVariablesDictionary; }
        }

        internal LoadVariables(Type itemType, Query dynamicQuery)
        {
            Type = itemType;
            DynamicQuery = dynamicQuery;

            Table = BusinessObjectLinqBase<T>.GetTable(Type);
            PropertySetterDictionary = BusinessObjectLinqBase<T>.GetPropertySetterDictionary(Type, Table);
            FieldDictionary = BusinessObjectLinqBase<T>.GetFieldDictionary(Type, Table);

            Container = dynamicQuery.MainContainer;

            MainTableProperties = dynamicQuery.GetProperties(Container);
            VirtualContainers = dynamicQuery.GetVirtualContainers(Container);

            //ObjectCounter.Add(this);
        }

        /// <summary>
        /// Gets the type.
        /// </summary>
        /// <value>The type.</value>
        public Type Type { get; private set; }
        /// <summary>
        /// Gets the table.
        /// </summary>
        /// <value>The table.</value>
        public Table Table { get; internal set; }
        /// <summary>
        /// Gets the property setter dictionary.
        /// </summary>
        /// <value>The property setter dictionary.</value>
        public Dictionary<string, MethodInfo> PropertySetterDictionary { get; internal set; }
        /// <summary>
        /// Gets the field dictionary.
        /// </summary>
        /// <value>The field dictionary.</value>
        public Dictionary<string, FieldInfoWrapper> FieldDictionary { get; internal set; }
        /// <summary>
        /// Gets the container.
        /// </summary>
        /// <value>The container.</value>
        public Container Container { get; internal set; }
        /// <summary>
        /// Gets the main table properties.
        /// </summary>
        /// <value>The main table properties.</value>
        public IEnumerable<Property> MainTableProperties { get; internal set; }
        /// <summary>
        /// Gets the virtual containers.
        /// </summary>
        /// <value>The virtual containers.</value>
        public IEnumerable<Container> VirtualContainers { get; internal set; }
        /// <summary>
        /// Gets the dynamic query.
        /// </summary>
        /// <value>The dynamic query.</value>
        public Query DynamicQuery { get; private set; }

        internal static void Remove(Query dynamicQuery)
        {
            lock (m_LoadVariablesDictionaryLocker)
            {
                if (dynamicQuery.Users != 0)
                    throw new Exception("dynamicQuery has users");

                foreach (ILoadVariables loadVariable in m_LoadVariablesDictionary[dynamicQuery].Values)
                {
                    if (loadVariable.DynamicQuery == dynamicQuery)
                    {
                        LoadVariables<T> load = ((LoadVariables<T>)loadVariable);
                        load.DynamicQuery = null;
                        load.Container = null;
                        load.MainTableProperties = null;
                        load.VirtualContainers = null;
                        load.FieldDictionary = null;
                        load.PropertySetterDictionary = null;
                        load.Table = null;
                    }
                }

                m_LoadVariablesDictionary[dynamicQuery].Clear();
                m_LoadVariablesDictionary.Remove(dynamicQuery);
            }
        }

        internal static ILoadVariables Get(Type type, Query dynamicQuery)
        {
            ILoadVariables loadVariables = null;
            lock (m_LoadVariablesDictionaryLocker)
            {
                Dictionary<Type, ILoadVariables> bizLoad;
                bool found = false;
                if (m_LoadVariablesDictionary.TryGetValue(dynamicQuery, out bizLoad))
                {
                    if (bizLoad.TryGetValue(type, out loadVariables))
                    {
                        found = true;
                    }
                }

                if (!found)
                {
                    loadVariables = new LoadVariables<T>(type, dynamicQuery);
                    if (bizLoad == null)
                    {
                        bizLoad = new Dictionary<Type, ILoadVariables>();
                        m_LoadVariablesDictionary.Add(dynamicQuery, bizLoad);
                    }
                    bizLoad.Add(type, loadVariables);
                }
            }

            return loadVariables;
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="BusinessObjectLinq&lt;T&gt;"/> is reclaimed by garbage collection.
        /// </summary>
        ~LoadVariables()
        {
            //ObjectCounter.Remove(this);
            Dispose();
        }

        private void Dispose()
        {
            Container = null;
            DynamicQuery = null;
        }
    }

    /// <summary>
    /// LoadFromDataRow ILoadVariables interface
    /// </summary>
    public interface ILoadVariables
    {
        /// <summary>
        /// Gets the type.
        /// </summary>
        /// <value>The type.</value>
        Type Type { get; }
        /// <summary>
        /// Gets the table.
        /// </summary>
        /// <value>The table.</value>
        Table Table { get; }
        /// <summary>
        /// Gets the property setter dictionary.
        /// </summary>
        /// <value>The property setter dictionary.</value>
        Dictionary<string, MethodInfo> PropertySetterDictionary { get; }
        /// <summary>
        /// Gets the field dictionary.
        /// </summary>
        /// <value>The field dictionary.</value>
        Dictionary<string, FieldInfoWrapper> FieldDictionary { get; }
        /// <summary>
        /// Gets the container.
        /// </summary>
        /// <value>The container.</value>
        Container Container { get; }
        /// <summary>
        /// Gets the main table properties.
        /// </summary>
        /// <value>The main table properties.</value>
        IEnumerable<Property> MainTableProperties { get; }
        /// <summary>
        /// Gets the virtual containers.
        /// </summary>
        /// <value>The virtual containers.</value>
        IEnumerable<Container> VirtualContainers { get; }
        /// <summary>
        /// Gets the dynamic query.
        /// </summary>
        /// <value>The dynamic query.</value>
        Query DynamicQuery { get; }
    }
}
